package com.caishi.lkx.exam.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.caishi.lkx.exam.mapper.rank.ExamQuestionRank0Mapper;
import com.caishi.lkx.exam.mapper.rank.ExamQuestionRankMapper;
import com.caishi.lkx.exam.model.ExamQuestionRank;
import com.caishi.lkx.exam.service.IExamQuestionRankService;
import com.zzw.common.Wrappers;
import com.zzw.common.entity.IBSMapper;
import com.zzw.common.entity.impl.BSDIntEntity;
import com.zzw.common.exception.BizRuntimeException;
import com.caishi.lkx.exam.ienum.type.RankType;

import com.caishi.lkx.exam.records.RecordsCollectType;

import lombok.SneakyThrows;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Service
public class ExamQuestionRankServiceImpl implements IExamQuestionRankService, ApplicationContextAware {

    private ApplicationContext applicationContext;

    private static final Map<String, IBSMapper<? extends ExamQuestionRank<?>, Long>> MAPPER_CACHE = new HashMap<>();
    private static final Map<String, Class<?>> CLASS_CACHE = new HashMap<>();

    @Resource
    private ExamQuestionRankMapper examQuestionRankMapper;

    @SneakyThrows
    @PostConstruct
    public void initLoadAllMapperBean() {
        for (var i = 0; i < 32; i++) {
            String clazzName = ExamQuestionRank0Mapper.class.getName().replace("ExamQuestionRank0", "ExamQuestionRank" + i);
            IBSMapper<? extends ExamQuestionRank<?>, Long> mapper = (IBSMapper<? extends ExamQuestionRank<?>, Long>) applicationContext.getBean(Class.forName(clazzName));
            MAPPER_CACHE.put(clazzName, mapper);
        }
    }

    @Override
    public IBSMapper<? extends ExamQuestionRank<?>, Long> routerMapper(ExamQuestionRank.ExamQuestionRankBaseModel superModel) {
        if (superModel.getExamId() == null) throw new BizRuntimeException();
        long hash = superModel.getExamId().hashCode() & 31;
        String clazzName = ExamQuestionRank0Mapper.class.getName().replace("ExamQuestionRank0", "ExamQuestionRank" + hash);
        IBSMapper<? extends ExamQuestionRank<?>, Long> mapper = MAPPER_CACHE.get(clazzName);
        try {
            if (mapper == null) {
                mapper = (IBSMapper<? extends ExamQuestionRank<?>, Long>) applicationContext.getBean(Class.forName(clazzName));
                MAPPER_CACHE.put(clazzName, mapper);
            }
        } catch (Exception ignore) {
        }
        return mapper == null ? examQuestionRankMapper : mapper;
    }

    @Override
    public ExamQuestionRank<?> routerEntity(ExamQuestionRank.ExamQuestionRankBaseModel superModel) {
        if (StrUtil.isBlank(superModel.getExamId())) throw new BizRuntimeException();
        long hash = superModel.getExamId().hashCode() & 31;
        String clazzName = String.format(ExamQuestionRank.ExamQuestionRank0.class.getName().replace("ExamQuestionRank0", "ExamQuestionRank" + hash));
        ExamQuestionRank<?> model = null;
        try {
            Class<?> clazz = CLASS_CACHE.get(clazzName);
            if (clazz == null) {
                clazz = Class.forName(clazzName);
                CLASS_CACHE.put(clazzName, clazz);
            }
            model = (ExamQuestionRank<?>) clazz.getDeclaredConstructor().newInstance();
        } catch (Exception ignore) {
        }
        if (model == null) throw new BizRuntimeException();
        BeanUtil.copyProperties(superModel, model);
        return model;
    }

    @Override
    public ExamQuestionRank.ExamQuestionRankBaseModel routerEntityTrans(ExamQuestionRank<?> entity) {
        var k = new ExamQuestionRank.ExamQuestionRankBaseModel();
        BeanUtil.copyProperties(entity, k);
        return k;
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }

    @Override
    public IPage<ExamQuestionRank.ExamQuestionRankBaseModel> rankData(String examId, Long categoryId, Page<ExamQuestionRank.ExamQuestionRankBaseModel> page, RankType type, String keyword, String currentQid) {
        int maxSize = 5000;
        if (page.offset() + page.getSize() >= maxSize) {
            var s = (double) maxSize / page.getSize();
            var f = Math.floor(s);
            page.setCurrent((long) (f == s ? f - 1 : f));
        }
        ExamQuestionRank.ExamQuestionRankBaseModel ltModel = null;
        if (StrUtil.isNotEmpty(currentQid)) {
            ltModel = selectFirst(ExamQuestionRank.ExamQuestionRankBaseModel.builder()
                            .examId(examId)
                            .build(),
                    wrappers()
                            .eq(categoryId != null, ExamQuestionRank::getCategoryId, categoryId)
                            .eq(ExamQuestionRank.ExamQuestionRankBaseModel::getExamId, examId)
                            .eq(ExamQuestionRank::getQuestionId, currentQid)
            );
        }
        IPage<ExamQuestionRank.ExamQuestionRankBaseModel> res;
        if (ltModel != null) {
            res = page(ExamQuestionRank.ExamQuestionRankBaseModel.builder()
                            .examId(examId)
                            .build(),
                    page,
                    wrappers()
                            .eq(ExamQuestionRank.ExamQuestionRankBaseModel::getExamId, examId)
                            .eq(categoryId != null, ExamQuestionRank.ExamQuestionRankBaseModel::getCategoryId, categoryId)
                            .le(type == RankType.all, ExamQuestionRank::getDoCount, ltModel.getDoCount())
                            .le(type == RankType.error, ExamQuestionRank::getErrorCount, ltModel.getErrorCount())
                            .le(type == RankType.collect, ExamQuestionRank::getCollectCount, ltModel.getCollectCount())
                            .ne(type == RankType.all, ExamQuestionRank::getDoCount, 0)
                            .ne(type == RankType.error, ExamQuestionRank::getErrorCount, 0)
                            .ne(type == RankType.collect, ExamQuestionRank::getCollectCount, 0)
                            .like(StrUtil.isNotEmpty(keyword), ExamQuestionRank::getStem, keyword)
                            .orderByDesc(type == RankType.all, ExamQuestionRank::getDoCount)
                            .orderByDesc(type == RankType.error, ExamQuestionRank::getErrorCount)
                            .orderByDesc(type == RankType.collect, ExamQuestionRank::getCollectCount)
                            .orderByAsc(BSDIntEntity::getId)
            );
        } else {
            res = page(ExamQuestionRank.ExamQuestionRankBaseModel.builder()
                            .examId(examId)
                            .build(),
                    page,
                    wrappers()
                            .eq(ExamQuestionRank.ExamQuestionRankBaseModel::getExamId, examId)
                            .eq(categoryId != null, ExamQuestionRank.ExamQuestionRankBaseModel::getCategoryId, categoryId)
                            .ge(type == RankType.all, ExamQuestionRank::getDoCount, 1)
                            .ge(type == RankType.error, ExamQuestionRank::getErrorCount, 1)
                            .ge(type == RankType.collect, ExamQuestionRank::getCollectCount, 1)
                            .like(StrUtil.isNotEmpty(keyword), ExamQuestionRank::getStem, keyword)
                            .orderByDesc(type == RankType.all, ExamQuestionRank::getDoCount)
                            .orderByDesc(type == RankType.error, ExamQuestionRank::getErrorCount)
                            .orderByDesc(type == RankType.collect, ExamQuestionRank::getCollectCount)
                            .orderByAsc(BSDIntEntity::getId)
            );
        }
        return res.setTotal(Math.min(res.getTotal(), maxSize));
    }

    @Override
    public void addDoCount(ExamQuestionRank.ExamQuestionRankBaseModel data, RecordsCollectType recordsCollectType) {
        Assert.notEmpty(data.getExamId());
        Assert.notNull(data.getCategoryId());
        Assert.notEmpty(data.getQuestionId());
        if (!update(data, Wrappers.<ExamQuestionRank.ExamQuestionRankBaseModel>lambdaUpdate()
                .setSql(data.getDoCount() != null && data.getDoCount() > 0, "do_count = do_count + " + data.getDoCount())
                .setSql(data.getErrorCount() != null && data.getErrorCount() > 0, "error_count = error_count + " + data.getErrorCount())
                .setSql(data.getCollectCount() != null && data.getCollectCount() > 0, " collect_count = collect_count + " + data.getCollectCount())
                .eq(ExamQuestionRank.ExamQuestionRankBaseModel::getExamId, data.getExamId())
                .eq(ExamQuestionRank::getCategoryId, data.getCategoryId())
                .eq(ExamQuestionRank::getQuestionId, data.getQuestionId())
        )) {
            if (recordsCollectType == RecordsCollectType.chapter || recordsCollectType == RecordsCollectType.paperRecordPaperLibrary || recordsCollectType == RecordsCollectType.paperRecordChapterPractice) {
                insert(data);
            }

        }
    }

    @Override
    public List<ExamQuestionRank.ExamQuestionRankBaseModel> isRank(String examId, Long categoryId, RankType type, String questionId) {
        return this.selectList(ExamQuestionRank.ExamQuestionRankBaseModel.builder()
                .examId(examId)
                .build(), Wrappers.<ExamQuestionRank.ExamQuestionRankBaseModel>lambdaQuery()
                .eq(ExamQuestionRank.ExamQuestionRankBaseModel::getCategoryId, categoryId)
                .eq(ExamQuestionRank.ExamQuestionRankBaseModel::getQuestionId, questionId)
                .ge(type == RankType.error, ExamQuestionRank.ExamQuestionRankBaseModel::getErrorCount, 1)
        );
    }

}

